package instructions

import (
	"fmt"
	"hi-jvm/instructions/base"
	"hi-jvm/instructions/constants"
	"hi-jvm/instructions/control"
	"hi-jvm/instructions/extended"
	"hi-jvm/instructions/loads"
	math_ "hi-jvm/instructions/math"
	mbase "hi-jvm/instructions/math/base"
	"hi-jvm/instructions/math/boolean"
	"hi-jvm/instructions/conversions"
	"hi-jvm/instructions/comparisons"
	"hi-jvm/instructions/stack"
	"hi-jvm/instructions/stores"
	"hi-jvm/rtda"
)

type CMDExexute interface {
	Execute(frame *rtda.Frame)
}

var (
	cmds map[byte]CMDExexute
)

func initCommands() {
	if len(cmds) > 0 {
		return
	}
	initCMDMap(0x00, func() CMDExexute {	return  &constants.NOP{}})
	initCMDMap(0x01, func() CMDExexute {	return  &constants.ACONST_NULL{}})
	initCMDMap(0x02, func() CMDExexute {	return  &constants.ICONST_M1{}})
	initCMDMap(0x03, func() CMDExexute {	return  &constants.ICONST_0{}})
	initCMDMap(0x04, func() CMDExexute {	return  &constants.ICONST_1{}})
	initCMDMap(0x05, func() CMDExexute {	return  &constants.ICONST_2{}})
	initCMDMap(0x06, func() CMDExexute {	return  &constants.ICONST_3{}})
	initCMDMap(0x07, func() CMDExexute {	return  &constants.ICONST_4{}})
	initCMDMap(0x08, func() CMDExexute {	return  &constants.ICONST_5{}})
	initCMDMap(0x09, func() CMDExexute {	return  &constants.LCONST_0{}})
	initCMDMap(0x0a, func() CMDExexute {	return  &constants.LCONST_1{}})
	initCMDMap(0x0b, func() CMDExexute {	return  &constants.FCONST_0{}})
	initCMDMap(0x0c, func() CMDExexute {	return  &constants.FCONST_1{}})
	initCMDMap(0x0d, func() CMDExexute {	return  &constants.FCONST_2{}})
	initCMDMap(0x0e, func() CMDExexute {	return  &constants.DCONST_0{}})
	initCMDMap(0x0f, func() CMDExexute {	return  &constants.DCONST_1{}})
	initCMDMap(0x10, func() CMDExexute {	return  &constants.BIPUSH{}})
	initCMDMap(0x11, func() CMDExexute {	return  &constants.SIPUSH{}})
	//initCMDMap(0x12, func() CMDExexute {	return  &LDC{}})
	//initCMDMap(0x13, func() CMDExexute {	return  &LDC_W{}})
	//initCMDMap(0x14, func() CMDExexute {	return  &LDC2_W{}})
	initCMDMap(0x15, func() CMDExexute {	return  &loads.ILOAD{}})
	initCMDMap(0x16, func() CMDExexute {	return  &loads.LLOAD{}})
	initCMDMap(0x17, func() CMDExexute {	return  &loads.FLOAD{}})
	initCMDMap(0x18, func() CMDExexute {	return  &loads.DLOAD{}})
	initCMDMap(0x19, func() CMDExexute {	return  &loads.ALOAD{}})
	initCMDMap(0x1a, func() CMDExexute {	return  &loads.ILOAD_0{}})
	initCMDMap(0x1b, func() CMDExexute {	return  &loads.ILOAD_1{}})
	initCMDMap(0x1c, func() CMDExexute {	return  &loads.ILOAD_2{}})
	initCMDMap(0x1d, func() CMDExexute {	return  &loads.ILOAD_3{}})
	initCMDMap(0x1e, func() CMDExexute {	return  &loads.LLOAD_0{}})
	initCMDMap(0x1f, func() CMDExexute {	return  &loads.LLOAD_1{}})
	initCMDMap(0x20, func() CMDExexute {	return  &loads.LLOAD_2{}})
	initCMDMap(0x21, func() CMDExexute {	return  &loads.LLOAD_3{}})
	initCMDMap(0x22, func() CMDExexute {	return  &loads.FLOAD_0{}})
	initCMDMap(0x23, func() CMDExexute {	return  &loads.FLOAD_1{}})
	initCMDMap(0x24, func() CMDExexute {	return  &loads.FLOAD_2{}})
	initCMDMap(0x25, func() CMDExexute {	return  &loads.FLOAD_3{}})
	initCMDMap(0x26, func() CMDExexute {	return  &loads.DLOAD_0{}})
	initCMDMap(0x27, func() CMDExexute {	return  &loads.DLOAD_1{}})
	initCMDMap(0x28, func() CMDExexute {	return  &loads.DLOAD_2{}})
	initCMDMap(0x29, func() CMDExexute {	return  &loads.DLOAD_3{}})
	initCMDMap(0x2a, func() CMDExexute {	return  &loads.ALOAD_0{}})
	initCMDMap(0x2b, func() CMDExexute {	return  &loads.ALOAD_1{}})
	initCMDMap(0x2c, func() CMDExexute {	return  &loads.ALOAD_2{}})
	initCMDMap(0x2d, func() CMDExexute {	return  &loads.ALOAD_3{}})
	//initCMDMap(0x2e, func() CMDExexute {	return  &loads.IALOAD{}})
	//initCMDMap(0x2f, func() CMDExexute {	return  &loads.LALOAD{}})
	//initCMDMap(0x30, func() CMDExexute {	return  &loads.FALOAD{}})
	//initCMDMap(0x31, func() CMDExexute {	return  &loads.DALOAD{}})
	//initCMDMap(0x32, func() CMDExexute {	return  &loads.AALOAD{}})
	//initCMDMap(0x33, func() CMDExexute {	return  &loads.BALOAD{}})
	//initCMDMap(0x34, func() CMDExexute {	return  &loads.CALOAD{}})
	//initCMDMap(0x35, func() CMDExexute {	return  &loads.SALOAD{}})
	initCMDMap(0x36, func() CMDExexute {	return  &stores.ISTORE{}})
	initCMDMap(0x37, func() CMDExexute {	return  &stores.LSTORE{}})
	initCMDMap(0x38, func() CMDExexute {	return  &stores.FSTORE{}})
	initCMDMap(0x39, func() CMDExexute {	return  &stores.DSTORE{}})
	initCMDMap(0x3a, func() CMDExexute {	return  &stores.ASTORE{}})
	initCMDMap(0x3b, func() CMDExexute {	return  &stores.ISTORE_0{}})
	initCMDMap(0x3c, func() CMDExexute {	return  &stores.ISTORE_1{}})
	initCMDMap(0x3d, func() CMDExexute {	return  &stores.ISTORE_2{}})
	initCMDMap(0x3e, func() CMDExexute {	return  &stores.ISTORE_3{}})
	initCMDMap(0x3f, func() CMDExexute {	return  &stores.LSTORE_0{}})
	initCMDMap(0x40, func() CMDExexute {	return  &stores.LSTORE_1{}})
	initCMDMap(0x41, func() CMDExexute {	return  &stores.LSTORE_2{}})
	initCMDMap(0x42, func() CMDExexute {	return  &stores.LSTORE_3{}})
	initCMDMap(0x43, func() CMDExexute {	return  &stores.FSTORE_0{}})
	initCMDMap(0x44, func() CMDExexute {	return  &stores.FSTORE_1{}})
	initCMDMap(0x45, func() CMDExexute {	return  &stores.FSTORE_2{}})
	initCMDMap(0x46, func() CMDExexute {	return  &stores.FSTORE_3{}})
	initCMDMap(0x47, func() CMDExexute {	return  &stores.DSTORE_0{}})
	initCMDMap(0x48, func() CMDExexute {	return  &stores.DSTORE_1{}})
	initCMDMap(0x49, func() CMDExexute {	return  &stores.DSTORE_2{}})
	initCMDMap(0x4a, func() CMDExexute {	return  &stores.DSTORE_3{}})
	initCMDMap(0x4b, func() CMDExexute {	return  &stores.ASTORE_0{}})
	initCMDMap(0x4c, func() CMDExexute {	return  &stores.ASTORE_1{}})
	initCMDMap(0x4d, func() CMDExexute {	return  &stores.ASTORE_2{}})
	initCMDMap(0x4e, func() CMDExexute {	return  &stores.ASTORE_3{}})
	//initCMDMap(0x4f, func() CMDExexute {	return  &stores.IASTORE{}})
	//initCMDMap(0x50, func() CMDExexute {	return  &stores.LASTORE{}})
	//initCMDMap(0x51, func() CMDExexute {	return  &stores.FASTORE{}})
	//initCMDMap(0x52, func() CMDExexute {	return  &stores.DASTORE{}})
	//initCMDMap(0x53, func() CMDExexute {	return  &stores.AASTORE{}})
	//initCMDMap(0x54, func() CMDExexute {	return  &stores.BASTORE{}})
	//initCMDMap(0x55, func() CMDExexute {	return  &stores.CASTORE{}})
	//initCMDMap(0x56, func() CMDExexute {	return  &stores.SASTORE{}})
	initCMDMap(0x57, func() CMDExexute {	return  &stack.POP{}})
	initCMDMap(0x58, func() CMDExexute {	return  &stack.POP2{}})
	initCMDMap(0x59, func() CMDExexute {	return  &stack.DUP{}})
	initCMDMap(0x5a, func() CMDExexute {	return  &stack.DUP_X1{}})
	initCMDMap(0x5b, func() CMDExexute {	return  &stack.DUP_X2{}})
	initCMDMap(0x5c, func() CMDExexute {	return  &stack.DUP2{}})
	initCMDMap(0x5d, func() CMDExexute {	return  &stack.DUP2_X1{}})
	initCMDMap(0x5e, func() CMDExexute {	return  &stack.DUP2_X2{}})
	initCMDMap(0x5f, func() CMDExexute {	return  &stack.SWAP{}})
	initCMDMap(0x60, func() CMDExexute {	return  &mbase.IADD{}})
	initCMDMap(0x61, func() CMDExexute {	return  &mbase.LADD{}})
	initCMDMap(0x62, func() CMDExexute {	return  &mbase.FADD{}})
	initCMDMap(0x63, func() CMDExexute {	return  &mbase.DADD{}})
	initCMDMap(0x64, func() CMDExexute {	return  &mbase.ISUB{}})
	initCMDMap(0x65, func() CMDExexute {	return  &mbase.LSUB{}})
	initCMDMap(0x66, func() CMDExexute {	return  &mbase.FSUB{}})
	initCMDMap(0x67, func() CMDExexute {	return  &mbase.DSUB{}})
	initCMDMap(0x68, func() CMDExexute {	return  &mbase.IMUL{}})
	initCMDMap(0x69, func() CMDExexute {	return  &mbase.LMUL{}})
	initCMDMap(0x6a, func() CMDExexute {	return  &mbase.FMUL{}})
	initCMDMap(0x6b, func() CMDExexute {	return  &mbase.DMUL{}})
	initCMDMap(0x6c, func() CMDExexute {	return  &mbase.IDIV{}})
	initCMDMap(0x6d, func() CMDExexute {	return  &mbase.LDIV{}})
	initCMDMap(0x6e, func() CMDExexute {	return  &mbase.FDIV{}})
	initCMDMap(0x6f, func() CMDExexute {	return  &mbase.DDIV{}})
	initCMDMap(0x70, func() CMDExexute {	return  &mbase.IREM{}})
	initCMDMap(0x71, func() CMDExexute {	return  &mbase.LREM{}})
	initCMDMap(0x72, func() CMDExexute {	return  &mbase.FREM{}})
	initCMDMap(0x73, func() CMDExexute {	return  &mbase.DREM{}})
	initCMDMap(0x74, func() CMDExexute {	return  &mbase.INEG{}})
	initCMDMap(0x75, func() CMDExexute {	return  &mbase.LNEG{}})
	initCMDMap(0x76, func() CMDExexute {	return  &mbase.FNEG{}})
	initCMDMap(0x77, func() CMDExexute {	return  &mbase.DNEG{}})
	initCMDMap(0x78, func() CMDExexute {	return  &math_.ISHL{}})
	initCMDMap(0x79, func() CMDExexute {	return  &math_.LSHL{}})
	initCMDMap(0x7a, func() CMDExexute {	return  &math_.ISHR{}})
	initCMDMap(0x7b, func() CMDExexute {	return  &math_.LSHR{}})
	initCMDMap(0x7c, func() CMDExexute {	return  &math_.IUSHR{}})
	initCMDMap(0x7d, func() CMDExexute {	return  &math_.LUSHR{}})
	initCMDMap(0x7e, func() CMDExexute {	return  &boolean.IAND{}})
	initCMDMap(0x7f, func() CMDExexute {	return  &boolean.LAND{}})
	initCMDMap(0x80, func() CMDExexute {	return  &boolean.IOR{}})
	initCMDMap(0x81, func() CMDExexute {	return  &boolean.LOR{}})
	initCMDMap(0x82, func() CMDExexute {	return  &boolean.IXOR{}})
	initCMDMap(0x83, func() CMDExexute {	return  &boolean.LXOR{}})
	initCMDMap(0x84, func() CMDExexute {	return  &math_.IINC{}})
	initCMDMap(0x85, func() CMDExexute {	return  &conversions.I2L{}})
	initCMDMap(0x86, func() CMDExexute {	return  &conversions.I2F{}})
	initCMDMap(0x87, func() CMDExexute {	return  &conversions.I2D{}})
	initCMDMap(0x88, func() CMDExexute {	return  &conversions.L2I{}})
	initCMDMap(0x89, func() CMDExexute {	return  &conversions.L2F{}})
	initCMDMap(0x8a, func() CMDExexute {	return  &conversions.L2D{}})
	initCMDMap(0x8b, func() CMDExexute {	return  &conversions.F2I{}})
	initCMDMap(0x8c, func() CMDExexute {	return  &conversions.F2L{}})
	initCMDMap(0x8d, func() CMDExexute {	return  &conversions.F2D{}})
	initCMDMap(0x8e, func() CMDExexute {	return  &conversions.D2I{}})
	initCMDMap(0x8f, func() CMDExexute {	return  &conversions.D2L{}})
	initCMDMap(0x90, func() CMDExexute {	return  &conversions.D2F{}})
	initCMDMap(0x91, func() CMDExexute {	return  &conversions.I2B{}})
	initCMDMap(0x92, func() CMDExexute {	return  &conversions.I2C{}})
	initCMDMap(0x93, func() CMDExexute {	return  &conversions.I2S{}})
	initCMDMap(0x94, func() CMDExexute {	return  &comparisons.LCMP{}})
	initCMDMap(0x95, func() CMDExexute {	return  &comparisons.FCMPL{}})
	initCMDMap(0x96, func() CMDExexute {	return  &comparisons.FCMPG{}})
	initCMDMap(0x97, func() CMDExexute {	return  &comparisons.DCMPL{}})
	initCMDMap(0x98, func() CMDExexute {	return  &comparisons.DCMPG{}})
	initCMDMap(0x99, func() CMDExexute {	return  &comparisons.IFEQ{}})
	initCMDMap(0x9a, func() CMDExexute {	return  &comparisons.IFNE{}})
	initCMDMap(0x9b, func() CMDExexute {	return  &comparisons.IFLT{}})
	initCMDMap(0x9c, func() CMDExexute {	return  &comparisons.IFGE{}})
	initCMDMap(0x9d, func() CMDExexute {	return  &comparisons.IFGT{}})
	initCMDMap(0x9e, func() CMDExexute {	return  &comparisons.IFLE{}})
	initCMDMap(0x9f, func() CMDExexute {	return  &comparisons.IF_ICMPEQ{}})
	initCMDMap(0xa0, func() CMDExexute {	return  &comparisons.IF_ICMPNE{}})
	initCMDMap(0xa1, func() CMDExexute {	return  &comparisons.IF_ICMPLT{}})
	initCMDMap(0xa2, func() CMDExexute {	return  &comparisons.IF_ICMPGE{}})
	initCMDMap(0xa3, func() CMDExexute {	return  &comparisons.IF_ICMPGT{}})
	initCMDMap(0xa4, func() CMDExexute {	return  &comparisons.IF_ICMPLE{}})
	initCMDMap(0xa5, func() CMDExexute {	return  &comparisons.IF_ACMPEQ{}})
	initCMDMap(0xa6, func() CMDExexute {	return  &comparisons.IF_ACMPNE{}})
	initCMDMap(0xa7, func() CMDExexute {	return  &control.GOTO{}})
	//initCMDMap(0xa8, func() CMDExexute {	return  &control.JSR{}})
	//initCMDMap(0xa9, func() CMDExexute {	return  &control.RET{}})
	initCMDMap(0xaa, func() CMDExexute {	return  &control.TABLE_SWITCH{}})
	initCMDMap(0xab, func() CMDExexute {	return  &control.LOOKUP_SWITCH{}})
	//initCMDMap(0xac, func() CMDExexute {	return  &control.IRETURN{}})
	//initCMDMap(0xad, func() CMDExexute {	return  &control.LRETURN{}})
	//initCMDMap(0xae, func() CMDExexute {	return  &control.FRETURN{}})
	//initCMDMap(0xaf, func() CMDExexute {	return  &control.DRETURN{}})
	//initCMDMap(0xbo, func() CMDExexute {	return  &control.ARETURN{}})
	//initCMDMap(0xb1, func() CMDExexute {	return  &control.RETURN{}})
	//initCMDMap(0xb2, func() CMDExexute {	return  &control.GET_STATIC{}})
	//initCMDMap(0xb3, func() CMDExexute {	return  &control.PUT_STATIC{}})
	//initCMDMap(0xb4, func() CMDExexute {	return  &control.GET_FIELD{}})
	//initCMDMap(0xb5, func() CMDExexute {	return  &control.PUT_FIELD{}})
	//initCMDMap(0xb6, func() CMDExexute {	return  &control.INVOKE_VIRTUAL{}})
	//initCMDMap(0xb7, func() CMDExexute {	return  &control.INVOKE_SPECIAL{}})
	//initCMDMap(0xb8, func() CMDExexute {	return  &control.INVOKE_STATIC{}})
	//initCMDMap(0xb9, func() CMDExexute {	return  &control.INVOKE_INTERFACE{}})
	//initCMDMap(0xba, func() CMDExexute {	return  &control.INVOKE_DYNAMIC{}})
	//initCMDMap(0xbb, func() CMDExexute {	return  &control.NEW{}})
	//initCMDMap(0xbc, func() CMDExexute {	return  &control.NEW_ARRAY{}})
	//initCMDMap(0xbd, func() CMDExexute {	return  &control.ANEW_ARRAY{}})
	//initCMDMap(0xbe, func() CMDExexute {	return  &control.arraylength{}})
	//initCMDMap(0xbf, func() CMDExexute {	return  &control.ATHROW{}})
	//initCMDMap(0xc0, func() CMDExexute {	return  &control.CHECK_CAST{}})
	//initCMDMap(0xc1, func() CMDExexute {	return  &control.INSTANCE_OF{}})
	//initCMDMap(0xc2, func() CMDExexute {	return  &control.MONITOR_EXIT{}})
	//initCMDMap(0xc3, func() CMDExexute {	return  &control.INVOKE_NATIVE{}})
	initCMDMap(0xc4, func() CMDExexute {	return  &extended.WIDE{}})
	//initCMDMap(0xc5, func() CMDExexute {	return  &control.MULTI_ANEW_ARRAY{}})
	initCMDMap(0xc6, func() CMDExexute {	return  &extended.IFNULL{}})
	initCMDMap(0xc7, func() CMDExexute {	return  &extended.IFNONNULL{}})
	initCMDMap(0xc8, func() CMDExexute {	return  &extended.GOTO_W{}})
	//initCMDMap(0xc9, func() CMDExexute {	return  &control.JSR_W{}})
	//initCMDMap(0xca, func() CMDExexute {	return  &breakpoint})
	//initCMDMap(0xfe, func() CMDExexute {	return  &impdep1})
	//initCMDMap(0xff, func() CMDExexute {	return  &impdep2})
}

func initCMDMap(code byte, fun func() CMDExexute) {
	cmd := fun()
	cmds[code] = CMDExexute(cmd)
}

func NewInstruction(opcode byte) base.Instruction {
	initCommands()
	v, ok := cmds[opcode]
	if !ok {
		panic(fmt.Errorf("Unsupported opcode: 0x%x!", opcode))
	}
	return v.(base.Instruction)
}

